import {RECORDER_INTERFACE, RECORDER_TYPE} from "./recorder_interface";
// @ts-ignore
import worklet_processor from "worklet-loader?inline!./recorder_worklet";

const audioWorklet = "audioWorklet";
const MediaRecorderTxt = "MediaRecorder";

class WorkletRecorder implements RECORDER_INTERFACE {
    private readonly processName = "worklet_recorder";
    private isConnected: boolean;
    private audioWorkletNode?: any;
    private streamSource?: any;
    private checkTimer?: any;
    private receiveFn?: (data: any) => void;
    private errorCb?: (e: any) => void;
    private processorConnected: boolean;
    private isStarted: boolean;

    constructor(private audioContext: any, private stream: any) {
        this.isConnected = false;
        this.isStarted = true;
        this.processorConnected = false;
    }

    getSampleRate(): number {
        return 16000;
    }

    getRecorderType(): number {
        return RECORDER_TYPE.WORKLET_RECORDER;
    }

    private createAudioWorkletNode() {
        let awNode = window.AudioWorkletNode;
        if (!this.isConnected) {
            return;
        }

        this.audioWorkletNode = new awNode(this.audioContext, this.processName);
        console.log("createAudioWorkletNode", this.audioWorkletNode);
        this.audioWorkletNode.port.onmessage = (e: MessageEvent) => {
            if (e.data.open) {
                this.processorConnected = true;
                if (this.checkTimer) {
                    clearTimeout(this.checkTimer);
                    this.checkTimer = undefined;
                }
            }else if (e.data.data) {
                if (this.receiveFn) {
                    this.receiveFn(e.data.data);
                }
            }
        }
        this.audioWorkletNode.port.postMessage({
            event: "REC_OPEN", data: {sampleRate: this.getSampleRate()}
        })
        this.streamSource = this.audioContext.createMediaStreamSource(this.stream);
        let dst:any;
        const cmsdTxt = "createMediaStreamDestination";
        if (this.audioContext[cmsdTxt]) {
            dst = this.audioContext[cmsdTxt]();
        }
        this.streamSource.connect(this.audioWorkletNode);
        this.audioWorkletNode.connect(dst);

        console.log("采用audio worklet");
    }

    connect(): Promise<boolean> {
        let url = worklet_processor;

        if (this.isSupport()) {
            return new Promise<boolean>((resolve, reject) => {
                this.isConnected = true;
                this.audioContext.resume().finally(() => {
                    console.log("audio context resume...");
                    if (!this.isConnected) {
                        reject("被打断了");
                        return;
                    }

                    console.log("processor url:", url);
                    this.audioContext[audioWorklet].addModule(url).then(() => {
                        if (!this.isConnected) {
                            reject("被打断了");
                            return;
                        }

                        this.processorConnected = false;
                        this.createAudioWorkletNode();
                        this.checkTimer = setTimeout(() => {
                            this.checkTimer = undefined;
                            if (this.isConnected) {
                                this.disconnect();
                                reject("录音器没反应");
                            }
                        }, 500);
                        resolve(true);
                    }).catch((reason: any) => {
                        console.log("addmodule fail:", reason);
                        this.disconnect();
                        reject(reason);
                    })
                });
            });
        }

        return Promise.reject("录音器不支持");
    }

    async disconnect(): Promise<boolean> {
        console.log("!!!!!disconnect");
        this.isConnected = false;
        this.processorConnected = false;

        if (this.checkTimer) {
            clearTimeout(this.checkTimer);
            this.checkTimer = undefined;
        }

        await this.stop();

        if (this.streamSource) {
            this.streamSource.disconnect();
            this.streamSource = null;
        }

        if (this.audioWorkletNode) {
            this.audioWorkletNode.port.postMessage({event: "REC_CLOSE"});
            this.audioWorkletNode.disconnect();
            this.audioWorkletNode = null;
        }

        return true;
    }

    isSupport(): boolean {
        let awNode: any = window.AudioWorkletNode;
        return this.audioContext[audioWorklet] && awNode;
    }

    onDataReceive(fn: (data: any) => void): void {
        this.receiveFn = fn;
    }

    onError(fn: (e: any) => void): void {
        this.errorCb = fn;
    }

    start(): Promise<boolean> {
        console.log("start", this.audioWorkletNode);
        if (this.audioWorkletNode) {
            this.audioWorkletNode.port.postMessage({event: "REC_START"});
            return Promise.resolve(true);
        }

        return Promise.reject("录音器未连接");
    }

    stop(): Promise<boolean> {
        if (this.audioWorkletNode) {
            this.audioWorkletNode.port.postMessage({event: "REC_STOP"});
            return Promise.resolve(true);
        }
        return Promise.reject("录音器未连接");
    }

}

export {WorkletRecorder};
